package com.itheima.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.itheima.constant.RedisConstant;
import com.itheima.dao.SetmealDao;
import com.itheima.entity.PageResult;
import com.itheima.entity.QueryPageBean;
import com.itheima.pojo.CheckItem;
import com.itheima.pojo.Setmeal;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import redis.clients.jedis.JedisPool;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 体检套餐服务
 */

@Service(interfaceClass = SetmealService.class)
@Transactional
public class SetmealServiceImpl implements SetmealService {
    @Autowired
    private SetmealDao setmealDao;
    @Autowired//操作redis的连接池(来自于spring-redis.xml里的jedisPool)
    private JedisPool jedisPool;
    @Autowired//把springg-service.xml里的freemarkerConfig注入
    private FreeMarkerConfigurer freemarkerConfigurer;
    @Value("${out_put_path}")//把属性文件freemarker.properties中的out_put_path注入
    private String outPutPath;//静态html页面存放路径

    //新增套餐，同时关联检查组
    @Override
    public void add(Setmeal setmeal, Integer[] checkgroupIds) {
        setmealDao.add(setmeal);
        Integer setmealId = setmeal.getId();//立即获取套餐id
        //<selectKey> 标签: 用于在执行插入操作后获取插入记录的主键（通常是自增长的 ID）
//<selectKey keyProperty="id" resultType="int" order="AFTER">
//<!-- resultType="int": 这个属性指定了 LAST_INSERT_ID() 的返回类型，即一个整数。-->
//<!-- order="AFTER": 表示在 insert 语句执行后再去获取最后插入的 ID，通常用于情况需要在插入记录后立即取得它的 ID。-->
//<!--使用 LAST_INSERT_ID() 获取自动生成的主键 ID-->

        //保存套餐和检查项的关联关系到t_setmeal_checkgroup表
        this.setSetmealAndCheckGroup(setmealId,checkgroupIds);
        //完成数据库操作后需要将图片名称保存到redis(这个是在七牛云和数据库都有记录的有用的图片)
        // (在七牛云一定有数据，但是在数据库不一定有记录的图片是垃圾图片)
        jedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());//sadd操作的是set集合

        //当添加套餐后需要重新生成静态页面(套餐列表页面，套餐详情页面)
        generateMobileStaticHtml();//this.generateMobileStaticHtml()???

    }

    //生成当前方法所需的静态页面
    public void generateMobileStaticHtml(){
        //生成套餐列表静态页面
        //在生成静态页面之前需要查询数据
        List<Setmeal> setmealList = setmealDao.findAll();

        //生成套餐列表静态页面
        generateMobileSetmealListHtml(setmealList);
        //生成套餐详情静态页面（多个）
        generateMobileSetmealDetailHtml(setmealList);
    }

  //生成套餐列表静态页面
  public void generateMobileSetmealListHtml(List<Setmeal> setmealList) {
    Map<String, Object> dataMap = new HashMap<String, Object>();
    dataMap.put("setmealList", setmealList);
    //key不能乱写，需要和模板文件mobile_setmeal_detail.ftl里
      // 的<#list setmealList as setmeal>对应
    this.generateStaticHtml("mobile_setmeal.ftl",
            "m_setmeal.html",dataMap);
  }

  //生成套餐详情静态页面（多个）
  public void generateMobileSetmealDetailHtml(List<Setmeal> setmealList) {
    for (Setmeal setmeal : setmealList) {
      Map<String, Object> dataMap = new HashMap<String, Object>();
        //findById()里调用了setmealDao.findById4Detail()查询套餐详情(检查组和检查项都要查出来)
      dataMap.put("setmeal", this.findById(setmeal.getId()));
      //key不能乱写，需要和模板文件mobile_setmeal_detail.ftl里
        // 的${setmeal.XXX}对应
      this.generateStaticHtml(
              "mobile_setmeal_detail.ftl",
              //每个套餐的对应详情页面不能一样，否则会覆盖
              //应该和模板文件里的超链接名称对应，否则无法跳转
              //<a class="link-page" href="setmeal_detail_${setmeal.id}.html">
              "setmeal_detail_"+setmeal.getId()+".html",
              dataMap);
    }
  }

    //通用方法用于生成静态页面                 模板名          静态页面名             数据
    public void generateStaticHtml(String templateName, String htmlPageName, Map map){
//缩减：
//        try {
//            //生成静态页面
//            freemarkerConfigurer.getConfiguration().getTemplate(templateName).process(map,
//                    //输出流，将生成的静态页面输出到指定的目录下
//                    //使用FileWriter将生成的静态页面输出到指定的目录下，但是不能指定编码，所以需要使用OutputStreamWriter
//                    new OutputStreamWriter(new FileOutputStream(new File(outPutPath+"/"+htmlPageName)),"UTF-8"));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        //获取配置对象
         Configuration configuration = freemarkerConfigurer.getConfiguration();
         Writer out = null;
        try {
            // 加载模版文件
            Template template = configuration.getTemplate(templateName);
            // 输出文件到磁盘(输出流)
            out = new FileWriter(new File(outPutPath+"/"+htmlPageName));
            // 输出文件
            template.process(map, out);
            out.close();
        } catch (Exception e) {
            //IOException改成Exception，统一处理getTemplate和process抛的异常
            e.printStackTrace();
          } finally {
            //finally 块总是会在 try 块执行完毕后执行，无论 try 块中是否发生了异常。
            // 因此，它通常用于清理操作，比如关闭资源（如文件、数据库连接等）。
      try {
          //在 finally 块中，还有一个 try 块，其目的是安全地处理潜在的异常。
          // 在这个 try 块内部，它检查 out 对象是否为 null。
        if (null != out) {
          //判断 out 是否已被初始化。如果 out 不为 null，
          // 说明它指向一个有效的对象，可以安全调用其方法。
          out.flush();
          //如果 out 有效，则调用 flush() 方法。
          // flush() 方法的作用是将任何缓冲的输出写入到目标流中。
          // 这通常在处理输出流时很关键，因为它确保所有的数据被实际写出，
            // 而不是停留在内存缓冲区中。
        }
      } catch (Exception e2) {
      //用于捕获 flush() 调用时可能发生的任何异常。如果在调用 flush() 时发生异常，
      // 它会被捕获，并通过 e2.printStackTrace(); 打印出异常的堆栈跟踪。
        e2.printStackTrace();
      }
    }
    }

    @Override
    public PageResult findPage(QueryPageBean queryPageBean) {
        Integer currentPage = queryPageBean.getCurrentPage();
        Integer pageSize = queryPageBean.getPageSize();
        String queryString = queryPageBean.getQueryString();//ThreadLocal
        PageHelper.startPage(currentPage,pageSize);//分页插件，会在执行sql之前将分页关键字追加到SQL后面
        Page<CheckItem> page = setmealDao.findByCondition(queryString);
        return new PageResult(page.getTotal(),page.getResult());
    }

    //查询所有套餐
    @Override
    public List<Setmeal> findAll() {
        return setmealDao.findAll();
    }

    //查询套餐详情
    @Override
    public Setmeal findById(Integer id) {
        //查询套餐表，将基本信息查询出来
        //根据套餐id查询关联的检查组，再将查询出的检查组集合赋值给套餐对象
        //根据检查组id查询关联的检查项集合，将集合赋值给检查组对象
        return setmealDao.findById4Detail(id);
    }

    //查询套餐占比统计数据
    @Override
    public List<Map> getSetmealReport() {
        return setmealDao.getSetmealReport();
    }

    //设置套餐和检查组多对多关联关系
    public void setSetmealAndCheckGroup(Integer setmealId,Integer[] checkgroupIds){
        if(checkgroupIds != null && checkgroupIds.length > 0){
            for (Integer checkgroupId : checkgroupIds) {
                Map<String,Integer> map = new HashMap<>();
                map.put("setmealId",setmealId);
                map.put("checkgroupId",checkgroupId);
                setmealDao.setSetmealAndCheckGroup(map);
            }
        }
    }
}
